<template>
  <div class="app-manage-search">
    <tiny-search v-model="state.pageSearchValue" clearable placeholder="搜索">
      <template #prefix>
        <tiny-icon-search />
      </template>
    </tiny-search>
  </div>

  <tiny-collapse v-model="state.collapseValue" class="page-manage-collapse lowcode-scrollbar">
    <tiny-collapse-item v-for="(groupItem, index) in pageSettingState.pages" :key="index" :name="groupItem.groupId">
      <template #title>
        <span class="title">{{ groupItem.groupName }}</span>
      </template>
      <div class="app-manage-tree">
        <draggable-tree
          :data="groupItem.data"
          label-key="name"
          :active="state.currentNodeData.id"
          :filter-value="state.pageSearchValue"
          :root-id="pageSettingState.ROOT_ID"
          :draggable="groupItem.groupId === STATIC_PAGE_GROUP_ID"
          @click-row="handleClickRow"
          @move-node="handleMoveNode"
        >
          <template #row-suffix="{ node }">
            <div :class="['actions']">
              <svg-button v-if="isPageLocked(node.rawData)" name="locked" :hoverBgColor="false"></svg-button>
              <svg-button v-if="node.rawData.isHome" name="home" :hoverBgColor="false"></svg-button>
              <tiny-popover
                :ref="(el) => setPopoverRef(el, node.id)"
                placement="bottom-start"
                :visible-arrow="false"
                popper-class="page-tree-row-operation-list"
              >
                <div class="operation-list">
                  <div
                    v-for="(operation, index) in getRowOperations(groupItem.groupId, node)"
                    :key="index"
                    :class="[operation.type === 'divider' ? 'divider' : 'item'].concat(operation.class || [])"
                    @click="operation.action?.(node)"
                  >
                    {{ operation.label }}
                  </div>
                </div>
                <template #reference>
                  <svg-button name="ellipsis" class="auto-hidden" :hoverBgColor="false"></svg-button>
                </template>
              </tiny-popover>
            </div>
          </template>
        </draggable-tree>
      </div>
    </tiny-collapse-item>
  </tiny-collapse>
</template>

<script lang="tsx">
/* metaService: engine.plugins.appmanage.PageTree */
import { reactive, onMounted, onUnmounted } from 'vue'
import { Search, Collapse, CollapseItem, Popover } from '@opentiny/vue'
import { IconFolderOpened, IconFolderClosed, IconSearch } from '@opentiny/vue-icon'
import {
  useCanvas,
  useModal,
  usePage,
  useNotify,
  useMessage,
  getMetaApi,
  getOptions,
  META_SERVICE
} from '@opentiny/tiny-engine-meta-register'
import { isEqual } from '@opentiny/vue-renderless/common/object'
import { getCanvasStatus } from '@opentiny/tiny-engine-common/js/canvas'
import { handlePageUpdate } from '@opentiny/tiny-engine-common/js/http'
import { constants } from '@opentiny/tiny-engine-utils'
import { closePageSettingPanel } from './PageSetting.vue'
import { closeFolderSettingPanel } from './PageFolderSetting.vue'
import http from './http'
import DraggableTree from './Tree.vue'
import { SvgButton } from '@opentiny/tiny-engine-common'
import meta from '../meta'

const { PAGE_STATUS } = constants

export default {
  components: {
    TinySearch: Search,
    TinyCollapse: Collapse,
    TinyCollapseItem: CollapseItem,
    TinyIconSearch: IconSearch(),
    TinyPopover: Popover,
    DraggableTree,
    SvgButton
  },
  props: {
    isFolder: {
      type: Boolean,
      default: false
    }
  },
  emits: ['openSettingPanel', 'add', 'createPage', 'createFolder', 'settingHome'],
  setup(props, { emit }) {
    const { confirm } = useModal()
    const { pageState, isBlock, isSaved } = useCanvas()
    const {
      pageSettingState,
      changeTreeData,
      isCurrentDataSame,
      getPageList,
      resetPageData,
      STATIC_PAGE_GROUP_ID,
      COMMON_PAGE_GROUP_ID,
      switchPage: switchPageById
    } = usePage()
    const { fetchPageDetail, requestUpdatePage } = http
    const getAppId = () => getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id
    const { customPageOperations = [] } = getOptions(meta.id)

    const state = reactive({
      pageSearchValue: '',
      collapseValue: [STATIC_PAGE_GROUP_ID, COMMON_PAGE_GROUP_ID],
      currentNodeData: { id: getMetaApi(META_SERVICE.GlobalService).getBaseInfo().pageId }
    })

    const { subscribe, unsubscribe } = useMessage()

    const refreshPageList = (appId: string) => getPageList(appId)

    let subscriber = null

    onMounted(() => {
      refreshPageList(getAppId())
      subscriber = subscribe({
        topic: 'locationHistoryChanged',
        callback: (data) => {
          if (data.pageId) {
            state.currentNodeData = { id: data.pageId }
          } else if (isBlock()) {
            state.currentNodeData = {}
          }
        },
        subscriber: 'pageTree'
      })
    })

    onUnmounted(() => {
      if (subscriber) {
        unsubscribe(subscriber)
      }
    })

    pageSettingState.updateTreeData = async () => {
      const pageList = await refreshPageList(getAppId())
      return pageList
    }

    const switchPage = (data) => {
      state.currentNodeData = data

      switchPageById(data.id).then(() => {
        closePageSettingPanel()
        closeFolderSettingPanel()
      })
    }

    const nodeClick = (e, pageData) => {
      e?.stopPropagation()

      const { id, isPage } = pageData

      // 区块切换回页面需要重新加载页面
      if ((!isBlock() && id === state?.currentNodeData?.id) || !isPage) {
        return
      }

      if (isSaved() && isCurrentDataSame()) {
        switchPage(pageData)
      } else {
        confirm({
          title: '提示',
          message: `${isBlock() ? '区块' : '页面'}尚未保存，是否要继续切换?`,
          exec: () => {
            changeTreeData(pageSettingState.oldParentId, pageSettingState.currentPageData.parentId)
            Object.assign(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)
            switchPage(pageData)
          }
        })
      }
    }

    const openSettingPanel = (e, pageData, isPageLocked) => {
      e?.stopPropagation()

      if (isPageLocked && pageData.isPage) {
        const username = pageData.occupier?.username || ''

        useModal().message({
          message: `您点击的页面被${username}锁定，暂时无法编辑，请联系解锁`
        })

        return
      }

      if (isEqual(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)) {
        emit('openSettingPanel', pageData)
      } else {
        confirm({
          title: '提示',
          message: `当前页面或文件夹${pageSettingState.currentPageData.name}尚未保存，是否要继续切换?`,
          exec: () => {
            changeTreeData(pageSettingState.oldParentId, pageSettingState.currentPageData.parentId)
            Object.assign(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)
            emit('openSettingPanel', pageData)
          }
        })
      }
    }

    const popoverRefs = {}
    const setPopoverRef = (el, nodeId) => {
      popoverRefs[nodeId] = el
    }

    const handleClickRow = (node) => {
      nodeClick(null, node.rawData)
    }

    const isPageLocked = (pageData) => {
      return getCanvasStatus(pageData.occupier).state === PAGE_STATUS.Lock
    }

    const handleClickPageSettings = (node) => {
      openSettingPanel(null, node.rawData, isPageLocked(node.rawData))
    }

    const createPage = (node) => {
      emit('createPage', 'staticPages', node.id)
    }

    const createFolder = (node) => {
      emit('createFolder', node.id)
    }

    const settingHome = (node) => {
      emit('settingHome', node)
    }

    const rowOperations = [
      { type: 'settings', label: '设置', action: handleClickPageSettings },
      { type: 'divider' },
      { type: 'createPage', label: '新建子页面', action: createPage },
      { type: 'createFolder', label: '新建子文件夹', action: createFolder },
      { type: 'settingHome', label: '设置为主页', action: settingHome },
      ...customPageOperations
      // TODO 复制和删除的逻辑耦合在其他组件内，暂时屏蔽
      // { type: 'divider' },
      // { type: 'copy', label: '复制页面', action: copyPage },
      // { type: 'delete', label: '删除', class: ['danger'], action: deleteNode }
    ].map((item) => ({
      ...item,
      action: (node) => {
        item.action?.(node, emit)
        // 点击 action 后，关闭 popover 弹窗
        popoverRefs[node.id]?.doClose?.()
      }
    }))

    const getRowOperations = (groupId, node) => {
      if (groupId === COMMON_PAGE_GROUP_ID) {
        return rowOperations.slice(0, 1)
      }
      if (!node.rawData.isPage || node.rawData.group === 'public' || String(node.parentId) !== '0') {
        return rowOperations.filter((item) => item.type !== 'settingHome')
      }
      // TODO 复制逻辑暂时屏蔽
      // if (!node.rawData.isPage) {
      //   return rowOperations.filter((item) => item.type !== 'copy')
      // }

      return rowOperations
    }

    const updatePage = (pageDetail) => {
      const { id, name, page_content } = pageDetail
      const params = {
        ...pageDetail,
        page_content: {
          ...page_content,
          fileName: name
        }
      }

      const isCurEditPage = pageState?.currentPage?.id === id
      const updateParams = {
        id,
        params,
        isCurEditPage
      }

      return handlePageUpdate(updateParams)
    }

    const updateFolder = (pageDetail) => {
      const { id } = pageDetail

      // requestUpdatePage加了then和catch回调函数，而handlePageUpdate没有加，是因为handlePageUpdate内部都已经有了类似逻辑
      return requestUpdatePage(id, { ...pageDetail, page_content: null })
        .then(() => {
          useNotify({
            type: 'success',
            message: '更新文件夹成功!'
          })
        })
        .catch((error) => {
          useNotify({
            type: 'error',
            title: '更新文件夹失败',
            message: JSON.stringify(error?.message || error)
          })
        })
        .finally(() => {
          pageSettingState.updateTreeData()
          pageSettingState.isNew = false
        })
    }

    const handleMoveNode = (dragged, newParent) => {
      if (isEqual(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)) {
        closePageSettingPanel()
        closeFolderSettingPanel()
        pageSettingState.currentPageData.id = dragged.id
        changeTreeData(newParent.id, dragged.parentId)
        resetPageData()
        // TODO 页面更换父节点后，原来每次变更需要填写变更信息
        fetchPageDetail(dragged.id)
          .then((pageDetail) => {
            pageDetail.parentId = newParent.id
            if (pageDetail.isPage) {
              pageDetail.isDefault = false
              updatePage(pageDetail)
            } else {
              updateFolder(pageDetail)
            }
          })
          .catch((error) => {
            useNotify({
              type: 'error',
              title: '移动页面文件/文件夹失败',
              message: JSON.stringify(error?.message || error)
            })
          })
      } else {
        confirm({
          title: '提示',
          message: '更改尚未保存，是否要放弃这些更改？',
          exec: () => {
            if (!pageSettingState.isNew) {
              changeTreeData(pageSettingState.oldParentId, pageSettingState.currentPageData.parentId)
              Object.assign(pageSettingState.currentPageData, pageSettingState.currentPageDataCopy)
            }
            closePageSettingPanel()
          }
        })
      }
    }

    useMessage().subscribe({
      topic: 'app_id_changed',
      subscriber: 'page_tree_app_id_changed',
      callback: (appId) => {
        refreshPageList(appId)
      }
    })

    const createPublicPage = (e) => {
      e.stopPropagation()
      e.preventDefault()
      emit('add')
    }

    onUnmounted(() => {
      useMessage().unsubscribe({
        topic: 'app_id_changed',
        subscriber: 'page_tree_app_id_changed'
      })
    })

    return {
      createPublicPage,
      state,
      switchPage,
      pageSettingState,
      setPopoverRef,
      IconFolderOpened: IconFolderOpened(),
      IconFolderClosed: IconFolderClosed(),
      getRowOperations,
      handleClickRow,
      handleMoveNode,
      isPageLocked,
      handleClickPageSettings,
      STATIC_PAGE_GROUP_ID
    }
  }
}
</script>

<style lang="less" scoped>
.app-manage-search {
  padding: 0 12px 12px 12px;
}

.page-manage-collapse {
  height: calc(100% - 95px);
  overflow-y: auto;
  .app-manage-public-page {
    position: absolute;
    right: 0;
    cursor: pointer;
    svg {
      font-size: 22px;
    }
  }
  :deep(.tiny-collapse-item__header) {
    &,
    &.is-active {
      &::before {
        border: none;
      }
    }
  }
  :deep(.tiny-collapse-item) {
    border-left: 0;
    border-right: 0;
  }
  :deep(.tiny-collapse-item__content) {
    padding: 0 0 12px 0;
  }
}

.page-manage-collapse.page-manage-collapse {
  :deep(.tiny-collapse-item__content) {
    padding-top: 0;
  }
}

.app-manage-tree {
  .actions {
    display: flex;
    align-items: center;
    svg {
      color: var(--te-page-manage-icon-color);
      outline: none;
    }
    .auto-hidden {
      visibility: hidden;
    }
  }
  .row:hover .actions .auto-hidden {
    visibility: visible;
  }
}
</style>

<style lang="less">
.tiny-popover.tiny-popper[x-placement].page-tree-row-operation-list {
  padding: 0;
  margin-top: 4px;
  .operation-list {
    min-width: 110px;
    padding: 8px 0;
    & > div {
      padding: 0 12px;
    }
    .item {
      height: 24px;
      font-size: 12px;
      line-height: 18px;
      cursor: pointer;
      display: flex;
      align-items: center;
      &:hover {
        background-color: var(--te-page-manage-operation-list-item-bg-color-hover);
      }
    }
    .divider {
      height: 8px;
      display: flex;
      align-items: center;
      &::after {
        content: '';
        display: block;
        width: 100%;
        height: 1px;
        background-color: var(--te-page-manage-operation-list-item-border-color-divider);
      }
    }
    .danger {
      color: var(--te-page-manage-operation-list-text-color-error);
    }
  }
}
</style>
